#include "variantmodel.h"
#include <QtCore/QStringList>
//#include <QtCore/QUrl>
//#include <QtCore/QLocale>
//#include <QtCore/QBitArray>

VariantModel::VariantModel(const QVariant &var, QObject *parent)
	: QAbstractItemModel(parent)
{
	setVariant(var);
}

VariantModel::~VariantModel()
{

}

void VariantModel::setVariant(const QVariant& var) 
{ 
	mVar = var;
	if (mVar.type()==QVariant::Map) {
		int index=0;
		foreach (QString var, mVar.toMap().keys()) {
			mMapped[index++]=var;
		}
	}
	reset(); 
}

QVariant VariantModel::value() const
{
	return mVar;
}

bool VariantModel::isCollection(QVariant::Type type)
{
	return (type==QVariant::List) 
		|| (type==QVariant::Map)
		|| (type==QVariant::StringList);
}

void VariantModel::append(QVariant value, const QString& key)
{
	Q_ASSERT(isCollection(mVar.type()));

	switch(mVar.type()) {
	case QVariant::StringList:
		Q_ASSERT(value.type()==QVariant::String);
		mVar = mVar.toStringList() << value.toString();
		reset();
		break;
	case QVariant::List:
		//trick! 
		//Reference: Only create QList<QVariant> one time
		mVar = mVar.toList() << value;  
		reset();
		break;
	case QVariant::Map:
		{QMap<QString, QVariant> temp = mVar.toMap();
		temp.insert(key, value);
		setVariant(temp);}
		break;
	default:
		;
	}
}

void VariantModel::deleteAt(const QModelIndex & index)
{
	Q_ASSERT(isCollection(mVar.type()));
	if (!index.isValid()) 
		return;

	switch(mVar.type()) {
	case QVariant::StringList:
		Q_ASSERT(index.row()<mVar.toStringList().size());
		{QStringList temp = mVar.toStringList();
		temp.removeAt(index.row());
		mVar = temp;}
		reset();
		break;
	case QVariant::List:
		Q_ASSERT(index.row()<mVar.toList().size());
		{QList<QVariant> temp = mVar.toList();
		temp.removeAt(index.row());
		mVar = temp;}
		reset();
		break;
	case QVariant::Map:
		Q_ASSERT(mMapped.contains(index.row()));
		{QString key = mMapped.value(index.row());
		QMap<QString, QVariant> temp = mVar.toMap();
		temp.remove(key);
		setVariant(temp);}
		break;
	default:
		;
	}
}

QVariant VariantModel::headerData ( int section, Qt::Orientation orientation, int role ) const
{
	if ((orientation == Qt::Horizontal) && (role == Qt::DisplayRole)) {
		if (mVar.type()!=QVariant::Map) //trick! 
			++section;
		
		switch(section) {
		case 0:
			return QVariant(tr("Key"));
		case 1:
			return QVariant(tr("Value"));
		default:
			return QVariant(tr("Type"));	
		}
	}

	return QAbstractItemModel::headerData(section, orientation, role);
}

int VariantModel::rowCount ( const QModelIndex & parent ) const
{
	switch (mVar.type()) {
	case QVariant::StringList:
	case QVariant::List:
		return mVar.toList().size();
	case QVariant::Map:
		return mVar.toMap().size();
	default:
		return 1;
	}
}

int VariantModel::columnCount ( const QModelIndex & parent ) const
{
	switch (mVar.type()){
	case QVariant::Map:
		return 3;
	default:
		return 2;
	}
}
QVariant VariantModel::data ( const QModelIndex & index, int role ) const
{
	if (!index.isValid()) 
		return QVariant();

	if (role!=Qt::DisplayRole && role!=Qt::EditRole) 
		return QVariant();

	QVariant var;
	switch(mVar.type()) {
	case QVariant::StringList:
	case QVariant::List:
		var = mVar.toList()[index.row()];
		if (index.column()==0) 
			return var;
		else
			return var.typeToName(var.type());
	case QVariant::Map:
		var = mVar.toMap()[mMapped[index.row()]];
		switch(index.column()) {
		case 0:
			return mMapped[index.row()];
		case 1:
			return var;
		default:
			return var.typeToName(var.type());
		} 
	default:
		if (index.column()==0) 
			return mVar;
		else
			return mVar.typeToName(mVar.type());

	}
}

QModelIndex VariantModel::index ( int row, int column, const QModelIndex & parent ) const
{
	Q_ASSERT(!parent.isValid());

	switch(mVar.type()) {
	case QVariant::StringList:
	case QVariant::List:
		return createIndex(row, column, 0); //NULL
	case QVariant::Map:
		return createIndex(row, column, 0); //NULL
	default:
		return createIndex(row, column, 0); //NULL
	}
}

QModelIndex VariantModel::parent ( const QModelIndex & index ) const
{
	return QModelIndex();
}

Qt::ItemFlags VariantModel::flags(const QModelIndex &index) const
{
	if (!index.isValid())
		return Qt::ItemIsEnabled;

	if ((mVar.type()==QVariant::StringList && index.column()==1) ||
		(isCollection(data(index).type()) && index.column()==(columnCount()-2)))
		return QAbstractItemModel::flags(index); 
	
	return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
}

//Why without emit dataChanged(), it still works well?
bool VariantModel::setData ( const QModelIndex & index, const QVariant & value, int role )
{
	if (!index.isValid() || role != Qt::EditRole)
		return false;

	switch(mVar.type()) {
	case QVariant::StringList:
		Q_ASSERT(index.column()==0);
		{QStringList temp = mVar.toStringList();
		temp[index.row()] = value.toString();
		mVar = temp;}
		break;
	case QVariant::List:
		{QList<QVariant> temp = mVar.toList();
		if (index.column()==0)
			temp[index.row()] = value;
		else 
			convert(temp[index.row()], value.toInt());
		mVar = temp;}
		break;
	case QVariant::Map:
		{QMap<QString, QVariant> temp = mVar.toMap();
		switch(index.column()) {
		case 0:
			{QVariant var = temp[mMapped[index.row()]];
			temp.remove(mMapped[index.row()]);
			temp.insert(value.toString(), var);}
			break;
		case 1:
			temp[mMapped[index.row()]] = value;
			break;
		default:
			convert(temp[mMapped[index.row()]], value.toInt());
		}
		setVariant(temp);}
		break;
	default:
		if (index.column()==0) 
			mVar = value;
		else 
			convert(mVar, value.toInt());
	}

	return true;
}

//just return true indeed.
bool VariantModel::convert(QVariant& var, int type)
{
	QVariant::Type expectType = static_cast<QVariant::Type>(type);
	switch(type) {
	case QVariant::StringList:
	case QVariant::List:
	case QVariant::Map:
		//if make this model to be a collection type,
		//trigger all views to update all their retrieve data.
		if (var.canConvert(expectType))
			var.convert(expectType);  //trick! return true indeed.
		else
			var = QVariant(expectType);

		if (mVar == var)
			setVariant(var);
			//emit toCollectionType();
		break;
	//case QVariant::BitArray: //the following three types can't be derived from Qt provided mechanism.
	//	var = QVariant(QBitArray(32));
	//	break;
	//case QVariant::Url:
	//	var = QVariant(QUrl("http://sites.google.com/site/dunanshan"));
	//	break;
	//case QVariant::Locale:
	//	var = QVariant(QLocale(QLocale::Chinese));
	//	break;
	default:
		if (var.canConvert(expectType)) 
			var.convert(expectType);
		else
			var = QVariant(expectType);
	}
	return true;
}